package org.example.auth.service.impl;

import org.example.auth.contract.request.RoleRequestForQuery;
import org.example.auth.contract.request.RoleRequestForSave;
import org.example.auth.contract.request.RoleRequestForUserBind;
import org.example.auth.contract.response.RoleResponse;
import org.example.auth.contract.response.RoleResponseForUser;
import org.example.auth.convert.RoleConvert;
import org.example.auth.enums.AuthExceptionEnum;
import org.example.auth.model.entity.Role;
import org.example.auth.model.entity.UserRole;
import org.example.auth.repository.RoleRepository;
import org.example.auth.repository.UserRoleRepository;
import org.example.auth.service.RoleAuthorityService;
import org.example.auth.service.RoleService;
import org.example.auth.service.UserRoleService;
import org.example.auth.service.UserService;
import org.example.misc.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class RoleServiceImpl implements RoleService {

    @Autowired
    private RoleRepository roleRepository;

    @Autowired
    private UserRoleRepository userRoleRepository;
    @Autowired
    private RoleAuthorityService roleAuthorityService;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private UserService userService;
    @Autowired
    private RoleConvert roleConvert;

    @Override
    @Transactional(rollbackOn = Throwable.class)
    public Role save(RoleRequestForSave request) {
        Role role = roleRepository.findByCode(request.getCode()).orElse(null);
        if (role != null) {
            throw new ServiceException(AuthExceptionEnum.ROLE_EXISTED);
        }
        return roleRepository.save(roleConvert.clone(request));
    }

    @Override
    public List<Role> getRoles(RoleRequestForQuery request) {
        Specification<Role> specification = (root, query, criteriaBuilder) -> {
            // 查询条件的集合
            List<Predicate> predicates = new ArrayList<>();
            if (request.getUserId() != null) {
                List<UserRole> userRoles = userRoleRepository.findByUserId(request.getUserId());
                if (!CollectionUtils.isEmpty(userRoles)) {
                    List<Long> roleIdsOfUser = userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList());
                    CriteriaBuilder.In<String> in = criteriaBuilder.in(root.get("id"));
                    in.in(roleIdsOfUser);
                    predicates.add(in);
                }
            }
            if (request.getParentId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("parentId"), request.getParentId()));
            }
//            predicates.add(criteriaBuilder.isTrue(root.get("commonInfo").get("enabled")));
            return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
        };
        return roleRepository.findAll(specification);
    }

    @Override
    @Transactional(rollbackOn = Throwable.class)
    public void deleteRole(Long id) {
        Role role = roleRepository.findById(id).orElseThrow(() -> new ServiceException("05001", "权限不存在！"));
        roleRepository.delete(role);
        // 解绑角色权限关系
        roleAuthorityService.deleteRoleAuthorityByRoleId(id);
        // 更新用户角色关系
        userRoleService.refreshForDeleteRoleId(id);

    }


    @Override
    public RoleResponseForUser groupRolesForUser(Long userId) {
        userService.getUserByIdWithException(userId);
        RoleResponseForUser response = new RoleResponseForUser();
        response.setUserId(userId);
        List<Role> roleList = roleRepository.findAll();
        List<UserRole> userRoles = userRoleRepository.findByUserId(userId);
        Set<Long> bindRoleIds = new HashSet<>();
        for (UserRole userRole : userRoles) {
            if (userRole.getDefaultFlag()) {
                response.setDefaultRoleId(userRole.getRoleId());
            }
            bindRoleIds.add(userRole.getRoleId());
        }
        List<RoleResponse> bindUserRoles = new ArrayList<>();
        List<RoleResponse> unBindUserRoles = new ArrayList<>();
        for (Role role : roleList) {
            if (bindRoleIds.contains(role.getId())) {
                bindUserRoles.add(roleConvert.clone(role));
            } else {
                unBindUserRoles.add(roleConvert.clone(role));
            }
        }
        response.setBindRoles(bindUserRoles);
        response.setUnBindRoles(unBindUserRoles);
        return response;
    }

    @Override
    public UserRole getDefaultRoleByUserId(Long userId) {
        return userRoleRepository.findByUserIdAndDefaultFlagTrue(userId).orElse(null);
    }
}
